// Mesh Format Loader B3D
// (c) jimon game studio

#ifndef JEH_CMESHFORMATLOADER_B3D
#define JEH_CMESHFORMATLOADER_B3D

#include "CMeshLoader.h"

#ifdef JE_SUPPORT_FILEFORMAT_B3D

#include "IMeshFormatLoader.h"

namespace je
{
	namespace scene
	{
		//! B3D Format Loader
		class CMeshFormatLoader_B3D:public je::scene::IMeshFormatLoader
		{
		protected:
			struct B3DHeader
			{
				c8 Name[4];
				s32 Length;
			};

			struct B3DTexture
			{
				jeStringc FileName;
				s32 Flags,Blend;
				jeVector2df Pos,Scale;
				f32 Rotation;
			};

			struct B3DBrush
			{
				jeStringc Name;
				video::jeColorARGB32f Color;
				f32 Shininess;
				s32 Blend,Fx;
				s32 NumOfTextures;
				s32 * TextureIds;

				B3DBrush()
					:TextureIds(NULL)
				{
				}

				~B3DBrush()
				{
					if(TextureIds)
						JEDELETE_A(TextureIds)
				}
			};

			struct B3DSurface
			{
				s32 Flags,TexCoordsSets,TexCoordSetSize;
				video::IHardwareBuffer * VB;

				B3DSurface()
					:VB(NULL)
				{
				}

				~B3DSurface()
				{
					VB->Drop();
				}
			};

			struct B3DSurfaceIndexes
			{
				s32 BrushId;
				video::IHardwareBufferArray * Array;
				
				B3DSurfaceIndexes()
					:Array(NULL)
				{
				}

				~B3DSurfaceIndexes()
				{
					Array->Drop();
				}
			};

			struct B3DBone
			{
				s32 VertexId;
				f32 Weight;
			};

			struct B3DKey
			{
				s32 Frame;
				jeVector3df Position;
				jeVector3df Scale;
				jeQuaternionf Rotation;

				B3DKey()
					:Frame(0)
				{
				}
			};

			struct B3DAnimation
			{
				s32 Flags,Frames;
				f32 Fps;
			};

			struct B3DMesh
			{
				s32 BrushId;
				B3DSurface * Surface;
				jeDynamicArray<B3DSurfaceIndexes*> SurfacesIndexes;

				B3DMesh()
					:Surface(NULL)
				{
				}

				~B3DMesh()
				{
					JEDELETE(Surface)
					for(u32 i = 0;i<SurfacesIndexes.GetSize();i++)
						JEDELETE(SurfacesIndexes[i])
					SurfacesIndexes.Clear();
				}
			};

			struct B3DNode
			{
				jeStringc Name;
				jeVector3df Position,Scale;
				jeQuaternionf Rotation;
				B3DMesh * Mesh;
				jeDynamicArray<B3DBone*> Bones;
				jeDynamicArray<B3DKey*> Keys;
				jeDynamicArray<B3DNode*> Nodes;
				B3DAnimation * Animation;

				B3DNode()
					:Mesh(NULL),Animation(NULL)
				{
				}

				~B3DNode()
				{
					if(Mesh)
						JEDELETE(Mesh)
					for(u32 i = 0;i<Bones.GetSize();i++)
						JEDELETE(Bones[i])
					Bones.Clear();
					for(u32 i = 0;i<Keys.GetSize();i++)
						JEDELETE(Keys[i])
					Keys.Clear();
					for(u32 i = 0;i<Nodes.GetSize();i++)
						JEDELETE(Nodes[i])
					Nodes.Clear();
					if(Animation)
						JEDELETE(Animation)
				}
			};

			struct B3DFile
			{
				B3DHeader * Header;
				s32 Version;
				jeDynamicArray<B3DTexture*> Textures;
				jeDynamicArray<B3DBrush*> Brushes;
				B3DNode * Node;

				B3DFile()
					:Node(NULL)
				{
				}

				~B3DFile()
				{
					JEDELETE(Header)
					for(u32 i = 0;i<Textures.GetSize();i++)
						JEDELETE(Textures[i])
					Textures.Clear();
					for(u32 i = 0;i<Brushes.GetSize();i++)
						JEDELETE(Brushes[i])
					Brushes.Clear();
					if(Node)
						JEDELETE(Node)
				}
			};

			void CorrentQuaternion(jeQuaternionf & Quat)
			{
				f32 W = Quat.X;
				Quat.X = -Quat.Y;
				Quat.Y = -Quat.Z;
				Quat.Z = -Quat.W;
				Quat.W = W;
			}

			B3DHeader * ReadHeader(core::IBuffer * DataBuffer);
			B3DTexture * ReadTexture(core::IBuffer * DataBuffer);
			B3DBrush * ReadBrush(core::IBuffer * DataBuffer,s32 NumOfTextures);
			B3DSurface * ReadSurface(core::IBuffer * DataBuffer,s32 SurfaceDistPos,video::IRender * Render);
			B3DSurfaceIndexes * ReadSurfaceIndexes(core::IBuffer * DataBuffer,B3DSurface * Surface,s32 SurfaceIndexesDistPos,video::IRender * Render);
			void ReadKey(B3DKey * Key,core::IBuffer * DataBuffer,s32 Flags,u1 ReadFrame = true);
			B3DBone * ReadBone(core::IBuffer * DataBuffer);
			B3DAnimation * ReadAnimation(core::IBuffer * DataBuffer);
			B3DNode * ReadNode(core::IBuffer * DataBuffer,s32 NodeDistPos,video::IRender * Render,s32 lvl=0);
			B3DFile * ReadFile(core::IBuffer * DataBuffer,video::IRender * Render);

			jeMesh * LoadMeshNode(B3DNode * Node,B3DFile * File,video::IRender * Render);
			jeSkeletonBone * LoadBones(B3DNode * Node,void * BonesData,u32 & BoneId);
			void MapBonesToSkeleton(jeSkeletonBone * Bone,jeSkeleton * Skeleton);
		public:
			//! Constructor
			CMeshFormatLoader_B3D();

			//! Destructor
			~CMeshFormatLoader_B3D();

			//! Load Mesh
			jeMesh * LoadMesh(core::IBuffer * DataBuffer,video::IRender * Render);

			//! Save Mesh
			void SaveMesh(core::IBuffer * DataBuffer,jeMesh * Mesh);

			//! Return true if mesh loader can work with this data
			u1 CanWorkWithThis(core::IBuffer * DataBuffer);

			//! Return Mesh Format
			MeshFormat GetMeshFormat()
			{
				return MF_B3D;
			}
		};
	}
}

#endif
#endif
